﻿using Red.Controls.Shell;
using Red.Controls.ViewModels;
using Red.EngineData.View;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Threading.Tasks;
using Red.Core.Containers;

namespace Red.Controls
{
    namespace ViewModels
    {
        internal class StringRef
        {
            public string val;

            public StringRef( string aVal )
            {
                val = aVal;
            }
        }

        internal class EngineActionParamRootItem : DataItem
        {
            #region Data

            private readonly DataView _view;
            private readonly StringRef[] _paramValues;
            private readonly string[] _paramNames;

            #endregion

            #region Properties

            public override string Type { get { return ""; } }

            public override DataItemKind Kind { get { return DataItemKind.Compound; } }

            public override string Value { get { return ""; } set { } }

            #endregion

            #region Constructor

            public EngineActionParamRootItem( DataView view, StringRef[] paramValues, string[] paramNames )
                : base( null )
            {
                Title = "Parameters";
                _view = view;
                _paramValues = paramValues;
                _paramNames = paramNames;
            }

            #endregion

            #region Protected Methods

            protected override Task EnumerateChildrenAsync( IRangedCollection<DataItem> children )
            {
                children.Clear();

                for ( int i = 0; i < _view.Children.Count; ++i )
                {
                    var paramName = String.Format( "Param {0} [{1}]", i, _view.Children[i] );
                    if ( _paramNames != null && _paramNames.Length > i )
                    {
                        paramName = _paramNames[i];
                    }

                    children.Add( new EngineActionParamItem( this, paramName, _view.Children[i], _paramValues[i] ) );
                }

                return Task.FromResult( true );
            }

            #endregion
        }

        internal class EngineActionParamItem : DataItem
        {
            #region Data

            private readonly string _type;
            private readonly StringRef _valueRef;

            #endregion

            #region Properties

            public override string Type { get { return _type; } }

            public override DataItemKind Kind { get { return DataItemKind.Simple; } }

            public override string Value
            {
                get { return _valueRef.val; }
                set
                {
                    if ( _valueRef.val != value )
                    {
                        _valueRef.val = value;
                        OnPropertyChanged();
                    }
                }
            }

            #endregion

            #region Constructor

            public EngineActionParamItem( DataItem parent, string name, string type, StringRef valueRef )
                : base( parent )
            {
                Title = name;
                _type = type;
                _valueRef = valueRef;
            }

            #endregion
        }

    }


    /// <summary>
    /// Interaction logic for RunEngineActionDialog.xaml
    /// </summary>
    public partial class RunEngineActionDialog : RedWindow, INotifyPropertyChanged
    {
        #region Data

        private readonly DataView _parentContext;
        private string _result = String.Empty;
        private StringRef[] _paramValues;

        #endregion Data

        #region Properties

        public DataView Functor { get; private set; }

        public string Result
        {
            get { return _result; }
            private set { _result = value; OnPropertyChanged(); }
        }

        #endregion Properties

        #region C-tor

        public RunEngineActionDialog( DataView parentContext, DataView functorContext, string[] paramNames = null )
        {
            DataContext = this;
            Functor = functorContext;
            _parentContext = parentContext;

            InitializeComponent();

            // Initialize param values wrapper - for now the default value is just empty string
            // If ActionView would provide the default values, they can be used here
            _paramValues = new StringRef[Functor.Children.Count];
            for ( int i = 0; i < Functor.Children.Count; ++i )
            {
                _paramValues[i] = new StringRef( "" );
            }

            var root = new EngineActionParamRootItem( Functor, _paramValues, paramNames );
            _parameters.DataContext = root;
            root.IsExpanded = true;

            _resultTextBlock.Visibility = Visibility.Hidden;
        }

        #endregion C-tor

        #region Event handlers

        private void RunAction_OnClick( object sender, RoutedEventArgs e )
        {
            List<string> textParams = new List<string>();
            string interopPath = Functor.InteropName + "(";
            for ( int i = 0; i < _paramValues.Length; ++i )
            {
                if ( i != 0 )
                {
                    interopPath += ",";
                }

                if ( _parentContext.Children[i] == "StringAnsi" )
                {
                    textParams.Add( _paramValues[i].val );
                    interopPath += "{" + ( textParams.Count - 1 ) + "}";
                }
                else
                {
                    interopPath += _paramValues[i].val;
                }
            }
            interopPath += ")";

            _parentContext.Execute( Interop.Make( interopPath, textParams.ToArray() ),
                result =>
                {
                    Result = result.Data;
                },
                ex =>
                {
                    Result = ex.Message;
                }
            );

            _resultTextBlock.Visibility = Visibility.Visible;
        }

        #endregion Event handlers

        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged( [CallerMemberName] string propertyName = null )
        {
            if ( PropertyChanged != null )
            {
                PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
            }
        }

        #endregion INotifyPropertyChanged
    }
}
